package com.joonhat.MonkeyFlash;


import net.rim.device.api.system.Backlight;
import net.rim.device.api.system.Characters;
import net.rim.device.api.system.Display;
import net.rim.device.api.system.KeyListener;
import net.rim.device.api.ui.Keypad;
import net.rim.device.api.ui.UiApplication;

import com.joonhat.MonkeyFlash.MyScreen.MyManager;
import com.joonhat.MonkeyFlash.hotkey.Hotkey;

interface HandlerContext {
	public abstract void setState(FlashModeState state);

	public abstract void setNextState(FlashModeState state);
}

public class FlashHandler implements KeyListener, HandlerContext {

	private static final FlashHandler handler = new FlashHandler();

	private MyManager manager = MyManager.getIns();
	private SimpleFlash simpleFlash;
	private Option option = Option.getIns();

	private LockKeyHandler lockKey = new LockKeyHandler();
	private static FlashModeState state;
	private static FlashModeState nextState;
	private long lastSpeakerKeyTime = 0;
	private long lastLockKeyTime = 0;

	private FlashHandler() {
		simpleFlash = SimpleFlash.getIns();
		SimpleFlash.setManager(manager);
		ModeChangeHandler.startup(this);
	}

	public static FlashHandler getIns() {
		return handler;
	}

	public boolean keyDown(int keycode, int j) {

		switch (Keypad.key(keycode)) {

		case Characters.SPACE:
			toggleFlash();
			break;

		case Characters.LATIN_CAPITAL_LETTER_N:
			changeMode();
			break;

		case Keypad.KEY_LOCK:
			Lock_KeyListener(j);
			break;

		case Keypad.KEY_SPEAKERPHONE:
			SpeakerPhone_KeyListener(j);
			break;
		}

		
		// P
		if (Keypad.key(keycode) == Characters.LATIN_CAPITAL_LETTER_K) {
			System.exit(0);
		}
		if (Keypad.key(keycode) == Characters.LATIN_CAPITAL_LETTER_Q) {
			
			
		}
		if (Keypad.key(keycode) == Characters.LATIN_CAPITAL_LETTER_A) {
		}

		return false;
	}

	public void OnOffButton_FieldChange() {
		toggleFlash();
	}
	public void HotkeyChoiceField_FieldChange(char selectedHotkey){
		Hotkey.replaceCurrentAppHotkey(selectedHotkey);
	}

	private void SpeakerPhone_KeyListener(int time) {
		if (checkSpeakerKeyOverlap(time))
			return;
		if (checkUnavailableState())
			return;
		toggleDanceFlash();
	}

	private void Lock_KeyListener(long time) {
		if (checkLockKeyOverlap(time))
			return;
		lockKey.handle();
	}

	private boolean checkUnavailableState() {
		return ScreenCtrl.getIns().isMyScreenDisplayed() == false
				&& DanceFlash.getThreadIns().isAlive() == false;
	}

	// GlobalScreen은 Foreground 로 인식되지 않으므로 addKeylistener(X, True);
	// 가 먹히지 않음 따라 화면이 떠있을때 중복으로 눌려지게 되는 문제를 해결하기위해
	// 2개의 입력중 1개의 키를 걸러주는 메소드
	private boolean checkSpeakerKeyOverlap(long time) {
		long repeatTime = (time - lastSpeakerKeyTime);
		lastSpeakerKeyTime = time;
		if (repeatTime < 150)
			return true;
		return false;
	}

	private boolean checkLockKeyOverlap(long time) {
		long repeatTime = (time - lastLockKeyTime);
		lastLockKeyTime = time;
		if (repeatTime < 150)
			return true;
		return false;
	}
	
	public boolean keyRepeat(int i, int j) {
		return false;
	}

	public boolean keyStatus(int i, int j) {
		return false;
	}

	public boolean keyUp(int i, int j) {
		return false;
	}

	public boolean keyChar(char c, int i, int j) {
		return false;
	}

	public void changeMode() {
		state.changeMode(this);
	}

	public void toggleFlash() {
		state.toggleFlash(this);
	}

	public void toggleDanceFlash() {
		state.toggleDanceFlash(this);
	}

	public void setState(FlashModeState state) {
		FlashHandler.state = state;
	}

	public void setNextState(FlashModeState state) {
		nextState = state;
		saveModeInfo(state);
	}

	private void saveModeInfo(FlashModeState state) {
		if (state == AnimateState.getInstance())
			return;
		if (state == SimpleState.getInstance()) {
			option.setNextMode(Option.SIMPLE_MODE);
			option.setLastMode(Option.SIMPLE_MODE);
		} else {
			option.setNextMode(Option.DANCE_MODE);
			option.setLastMode(Option.DANCE_MODE);
		}
	}

	public FlashModeState getNextState() {
		return nextState;
	}

}

class LockKeyHandler {
	// Lock키를 눌렀을때 Backlight.isEnable() 은 true 에서 false로는 반응이
	// 살짝 느림, 반면에 false에서 ture로는 반응이 빨라서 화면이 아직 안켜졌는데도
	// true값이 나옴/ Lock을했다가 해제할때는 Keylistener에서 감지하지 못함
	Thread thread;

	boolean beforeBacklight;
	long keyClickTime;

	public void handle() {
		interrupt();
		determineStartTimer();
	}

	private void determineStartTimer() {
		thread = new Thread() {
			public void run() {
				initBacklightAndTime();
				if (NoStartSituation())
					return;
				ModeChangeHandler.prepareLastMode();
			}
		};
		thread.start();
	}

	private boolean NoStartSituation() {
		return checkBacklightStateChange() == false
				|| Backlight.isEnabled() == false
				|| Option.getIns().getLastMode() == Option.DANCE_MODE;
	}

	private synchronized void interrupt() {
		if (thread == null || thread.isAlive() == false)
			return;
		thread.interrupt();
	}

	private boolean checkBacklightStateChange() {
		while ((System.currentTimeMillis() - keyClickTime) < 2000) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			if (beforeBacklight != Backlight.isEnabled())
				return true;
		}
		return false;
	}

	private void initBacklightAndTime() {
		beforeBacklight = Backlight.isEnabled();
		keyClickTime = System.currentTimeMillis();
	}
}

interface FlashModeState {
	public abstract void changeMode(HandlerContext context);

	public abstract void toggleDanceFlash(HandlerContext context);

	public void toggleFlash(HandlerContext context);
}

class SimpleState implements FlashModeState {

	private static SimpleState simpleState = new SimpleState();

	private SimpleState() {
	}

	public static SimpleState getInstance() {
		return simpleState;
	}

	public synchronized void changeMode(HandlerContext context) {
		ModeChangeHandler.changeForDance();
	}

	public void toggleFlash(HandlerContext context) {
		SimpleOption.getIns().setToggleFlash(true);
		if (SimpleFlash.getVideoField().getIndex() == -1) {
			toggleOn();
		} else {
			toggleOff();
		}
	}

	private void toggleOn() {
		SimpleOption.getIns().setCurrentFlashOn(true);
		FlashTimer.getIns().stopAndStart();
	}

	private void toggleOff() {
		SimpleOption.getIns().setCurrentFlashOn(false);
		FlashTimer.getIns().stop();
	}

	public void toggleDanceFlash(HandlerContext context) {
	}
}

class DanceState implements FlashModeState {

	private static DanceState danceState = new DanceState();

	private DanceState() {
	}

	public static DanceState getInstance() {
		return danceState;
	}

	public synchronized void changeMode(final HandlerContext context) {
		ModeChangeHandler.changeForSimple();
	}

	public void toggleFlash(HandlerContext context) {
		DanceFlash.toggleSimpleFlash();
	}

	public void toggleDanceFlash(HandlerContext context) {
		DanceFlash.toggleDanceFlash();
	}
}

class AnimateState implements FlashModeState {
	private static AnimateState animateState = new AnimateState();

	private AnimateState() {
	}

	public static AnimateState getInstance() {
		return animateState;
	}

	public void changeMode(HandlerContext context) {
	}

	public void toggleDanceFlash(HandlerContext context) {
	}

	public void toggleFlash(HandlerContext context) {
	}
}

class ModeChangeHandler {

	private static Object lock = new Object();

	public static void startup(FlashHandler handler) {
		if (isLastModeSimple()) {
			initSimpleMode(handler);
		} else {
			initDanceMode(handler);
		}

	}

	public static void prepareLastMode() {
		if (isLastModeSimple()) {
			prepareSimpleMode();
		} else {
			prepareDanceMode(FlashHandler.getIns());
		}
	}

	public static void closeCurrentMode() {
		closeCurrentMode(true);
	}

	public static void changeForDance() {
		closeCurrentMode(false);
		prepareDanceMode(FlashHandler.getIns());
		changeStateAndBeginAnimation(false);
	}

	public static void changeForSimple() {
		new Thread() {
			public void run() {
				synchronized (lock) {
					for (int i = 0; i < 5; i++) {
						if (DanceFlash.isVRForeground()) {
							closeCurrentMode(false);
							if (checkCloseFail())
								continue;
						}
						prepareSimpleMode();
						changeStateAndBeginAnimation(true);
						break;
					}
				}
			}

			private boolean checkCloseFail() {
				for (int i = 0; i < 15; i++) {
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					if (DanceFlash.isVRForeground())
						continue;
					else
						return false;
				}
				return true;
			}

		}.start();
	}

	private static void initDanceMode(FlashHandler handler) {
		prepareDanceMode(handler);
		AniEffect.setInt(-Display.getWidth());
		handler.setState(DanceState.getInstance());
	}

	private static void initSimpleMode(FlashHandler handler) {
		AniEffect.setInt(0);
		handler.setState(SimpleState.getInstance());
	}

	private static void closeCurrentMode(boolean exitApp) {
		if (isLastModeSimple()) {
			closeSimpleMode();
		} else {
			closeDanceMode(exitApp);
		}
	}

	private static void closeSimpleMode() {
		FlashTimer.getIns().stop();
	}

	private static void closeDanceMode(boolean endApp) {
		if (endApp) {
			closeViderRecorder();
		} else {
			DanceFlash.closeVideoRecorder();
		}
	}

	private static void prepareSimpleMode() {
		FlashHandler.getIns().setNextState(SimpleState.getInstance());
		SimpleOption.getIns().updateCurrentFlashFromStartupFlash();
		FlashTimer.getIns().stopAndStart();
	}

	private static void prepareDanceMode(FlashHandler handler) {
		handler.setNextState(DanceState.getInstance());
		DanceFlash.runVideoRecorder();
	}

	private static void closeViderRecorder() {
		if (DanceFlash.isVRForeground() == false)
			return;
		FlashKeyEvent.getIns().postEscKey();
	}

	private static boolean isLastModeSimple() {
		return Option.getIns().getLastMode() == Option.SIMPLE_MODE;
	}

	private static void changeStateAndBeginAnimation(boolean changeForSimple) {
		if (changeForSimple) {
			AniEffect.nextDirection(1);
		} else {
			AniEffect.nextDirection(-1);
		}
		AniEffect.begin(0);
	}
}